package com.houweishi.module.wageOperate.service.impl;

import com.houweishi.common.core.domain.AjaxResult;
import com.houweishi.common.excel.common.PoiWorkbookType;
import com.houweishi.common.excel.core.ExcelHelper;
import com.houweishi.common.utils.DateUtils;
import com.houweishi.common.utils.StringUtils;
import com.houweishi.common.utils.bean.BeanUtils;
import com.houweishi.module.leave.domain.OaLeaveApproval;
import com.houweishi.module.leave.mapper.OaLeaveMapper;
import com.houweishi.module.staff.domain.BizStaff;
import com.houweishi.module.staff.service.IBizStaffService;
import com.houweishi.module.wageOperate.domain.ClockCountDO;
import com.houweishi.module.wageOperate.domain.RenewalSearchVO;
import com.houweishi.module.wageOperate.domain.WageOperate;
import com.houweishi.module.wageOperate.domain.WageOperateSumVO;
import com.houweishi.module.wageOperate.mapper.WageOperateMapper;
import com.houweishi.module.wageOperate.service.IWageOperateService;
import com.houweishi.module.wageOperateHistroy.domain.WageOperateHistory;
import com.houweishi.module.wageOperateHistroy.service.IWageOperateHistoryService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工资计算Service业务层处理
 * 
 * @author yqq
 * @date 2023-06-20
 */
@Service
public class WageOperateServiceImpl implements IWageOperateService 
{
    @Resource
    private WageOperateMapper wageOperateMapper;

    /**
     * 员工service
     */
    @Resource
    private IBizStaffService iBizStaffService;

    /**
     * 请假oaLeaveMapper
     */
    @Resource
    private OaLeaveMapper oaLeaveMapper;

    /**
     * 工资历史记录
     */
    @Resource
    private IWageOperateHistoryService iWageOperateHistoryService;

    /**
     * 查询工资计算
     * 
     * @param id 工资计算主键
     * @return 工资计算
     */
    @Override
    public WageOperate selectWageOperateById(Long id)
    {
        return wageOperateMapper.selectWageOperateById(id);
    }

    /**
     * 查询工资计算列表
     * 
     * @param wageOperate 工资计算
     * @return 工资计算
     */
    @Override
    public List<WageOperate> selectWageOperateList(WageOperate wageOperate)
    {
        return wageOperateMapper.selectWageOperateList(wageOperate);
    }

    /**
     * 批量新增工资计算
     * @param wageOperateList 工资计算
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBathWageOperate(List<WageOperate> wageOperateList) {
        wageOperateMapper.insertBathWageOperate(wageOperateList);
    }

    /**
     * 新增工资计算
     * 
     * @param wageOperate 工资计算
     * @return 结果
     */
    @Override
    public int insertWageOperate(WageOperate wageOperate)
    {
        wageOperate.setCreateTime(DateUtils.getNowDate());
        return wageOperateMapper.insertWageOperate(wageOperate);
    }

    /**
     * 修改工资计算
     * 
     * @param wageOperate 工资计算
     * @return 结果
     */
    @Override
    public int updateWageOperate(WageOperate wageOperate)
    {
        wageOperate.setUpdateTime(DateUtils.getNowDate());
        return wageOperateMapper.updateWageOperate(wageOperate);
    }

    /**
     * 批量删除工资计算
     * 
     * @param ids 需要删除的工资计算主键
     * @return 结果
     */
    @Override
    public int deleteWageOperateByIds(Long[] ids)
    {
        return wageOperateMapper.deleteWageOperateByIds(ids);
    }

    /**
     * 删除工资计算信息
     * 
     * @param id 工资计算主键
     * @return 结果
     */
    @Override
    public int deleteWageOperateById(Long id)
    {
        return wageOperateMapper.deleteWageOperateById(id);
    }

    /**
     * 更新工资计算表数据
     * @return
     */
    @Override
    public AjaxResult renewalData() {
        //更新数据集合
        List<WageOperate> wageOperateList = new ArrayList<>();
        //获取员工表数据
        List<BizStaff> bizStaffs = iBizStaffService.selectBizStaffList(new BizStaff());
        //获取所有员工工资记录
        List<WageOperate> wageOperates = wageOperateMapper.selectAllWageOperateList();
        //获取需要更新的数据
        List<BizStaff> updateStaff = new ArrayList<>();
        for (BizStaff bizStaff : bizStaffs) {
            List<WageOperate> collect = wageOperates.stream().filter(wageOperate -> wageOperate.getStaffName().equals(bizStaff.getName())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)){
                updateStaff.add(bizStaff);
            }
        }
        if (!CollectionUtils.isEmpty(updateStaff)){
            for (BizStaff bizStaff : updateStaff) {
                WageOperate wageOperate = new WageOperate();
                wageOperate.setDepartmentId(bizStaff.getDept());
                wageOperate.setDepartmentName(bizStaff.getDeptName());
                wageOperate.setPostId(bizStaff.getPostId());
                wageOperate.setPostName(bizStaff.getPost());
                wageOperate.setStaffId(bizStaff.getId());
                wageOperate.setStaffName(bizStaff.getName());
                wageOperate.setUserId(bizStaff.getUserId());
                wageOperate.setUserName(bizStaff.getUserName());
                wageOperate.setEntryDate(bizStaff.getEntryTime());
                wageOperate.setLeaveDate(bizStaff.getLeaveDate());
                wageOperate.setStaffType(bizStaff.getStaffType());
                wageOperate.setCompany(bizStaff.getCompany());
                // 忠诚奖励:满一年之后，工龄*100，针对全职员工
                if ("全职".equals(bizStaff.getStaffType())){
                    LocalDate entryDate = bizStaff.getEntryTime();
                    Period period = Period.between(entryDate,LocalDate.now());
                    wageOperate.setLoyaltyReward(BigDecimal.valueOf(period.getYears()* 100L));
                }
                wageOperateList.add(wageOperate);
            }
        }
        if (!CollectionUtils.isEmpty(wageOperateList)){
            wageOperateMapper.insertBathWageOperate(wageOperateList);
        }
        return AjaxResult.success();
    }

    /**
     * date 转 localDate
     * @param date
     * @return
     */
    public LocalDate dateToLocalDate(Date date){
        if (date!=null){
            return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        }
        return null;
    }
    /**
     * 计算工资计算表考勤
     * @param renewalSearchVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult renewalClocking(RenewalSearchVO renewalSearchVO) {
        //获取需要修改的数据
        List<WageOperate> wageOperateList=wageOperateMapper.selectWageOperateListBySearchVo(renewalSearchVO);
        if (CollectionUtils.isEmpty(wageOperateList)){
            return AjaxResult.success("修改0条记录");
        }

        if (StringUtils.isBlank(renewalSearchVO.getRenewalDate())){
            return AjaxResult.error("请选择考勤年月");
        }
        //获取对应的考勤数据
        List<ClockCountDO> clockCountDOS = wageOperateMapper.selectClockCountList(renewalSearchVO);
        if (CollectionUtils.isEmpty(clockCountDOS)){
            return AjaxResult.success("暂无本月考勤数据");
        }
        Map<String, Integer> maps = clockCountDOS.stream().collect(Collectors.toMap(ClockCountDO::getUserName, ClockCountDO::getCount, (key1, key2) -> key2));
        //请假
        List<OaLeaveApproval> oaLeaveApprovals = oaLeaveMapper.selectOaLeaveListByStartDate(renewalSearchVO.getRenewalDate());
        //计算 全勤，出勤天数
        for (WageOperate wageOperate : wageOperateList) {
            //出勤天数
            Integer count = maps.get(wageOperate.getStaffName());
            wageOperate.setAttendanceDays(Objects.isNull(count)?0:count);
            //    全勤
            if ("全职".equals(wageOperate.getStaffType())){
                wageOperate.setFullAttendance(new BigDecimal("300.00"));
                if (!CollectionUtils.isEmpty(oaLeaveApprovals)){
                    List<String> collect = oaLeaveApprovals.stream().map(OaLeaveApproval::getStaffName).collect(Collectors.toList());
                    if (collect.contains(wageOperate.getStaffName())){
                        wageOperate.setFullAttendance(BigDecimal.ZERO);
                    }
                }
            }
        }
        wageOperateMapper.updateBathWageOperate(wageOperateList);
        return AjaxResult.success();
    }

    /**
     * 计算工资
     * @param renewalSearchVO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult calcSalary(RenewalSearchVO renewalSearchVO){
       //获取需要计算工资的数据
        List<WageOperate> wageOperateList=wageOperateMapper.selectWageOperateListBySearchVo(renewalSearchVO);
        if (CollectionUtils.isEmpty(wageOperateList)){
            return AjaxResult.success("修改0条记录");
        }
        for (WageOperate wageOperate : wageOperateList) {
            calcPay(wageOperate);
        }
        wageOperateMapper.updateBathWageOperate(wageOperateList);
        return AjaxResult.success();
    }

    /**
     * 计算工资合计
     * @param renewalSearchVO
     * @return
     */
    @Override
    public AjaxResult calcSalarySum(RenewalSearchVO renewalSearchVO) {
        List<WageOperate> wageOperateList=wageOperateMapper.selectWageOperateListBySearchVo(renewalSearchVO);
        WageOperateSumVO wageOperateSumVO = new WageOperateSumVO();
        BigDecimal payDueSum=BigDecimal.ZERO;
        BigDecimal socialSecuritySum= BigDecimal.ZERO;
        BigDecimal personalIncomeTaxSum=BigDecimal.ZERO;
        BigDecimal fineSum=BigDecimal.ZERO;
        BigDecimal otherSum= BigDecimal.ZERO;
        BigDecimal realWagesSum = BigDecimal.ZERO;
        if (!CollectionUtils.isEmpty(wageOperateList)){
            for (WageOperate wageOperate : wageOperateList) {
                //社保
                socialSecuritySum= socialSecuritySum.add(Objects.isNull(wageOperate.getSocialSecurity())?BigDecimal.ZERO:wageOperate.getSocialSecurity());
                //个税
                personalIncomeTaxSum= personalIncomeTaxSum.add(Objects.isNull(wageOperate.getPersonalIncomeTax())?BigDecimal.ZERO:wageOperate.getPersonalIncomeTax());
                // 罚款
                fineSum=fineSum.add(Objects.isNull(wageOperate.getFine())?BigDecimal.ZERO:wageOperate.getFine());
                // 其他
                otherSum= otherSum.add(Objects.isNull(wageOperate.getOther())?BigDecimal.ZERO:wageOperate.getOther());
                payDueSum= payDueSum.add(Objects.isNull(wageOperate.getPayDue())?BigDecimal.ZERO:wageOperate.getPayDue());
                realWagesSum= realWagesSum.add(Objects.isNull(wageOperate.getRealWages())?BigDecimal.ZERO:wageOperate.getRealWages());
            }
            wageOperateSumVO.setPayDueSum(payDueSum);
            wageOperateSumVO.setSocialSecuritySum(socialSecuritySum);
            wageOperateSumVO.setPersonalIncomeTaxSum(personalIncomeTaxSum);
            wageOperateSumVO.setFineSum(fineSum);
            wageOperateSumVO.setOtherSum(otherSum);
            wageOperateSumVO.setRealWagesSum(realWagesSum);
        }
        return AjaxResult.success(wageOperateSumVO);
    }

    /**
     * 计算出勤工资，实发工资，应发工资
     * @return
     */
    public WageOperate calcPay(WageOperate wageOperate){
        // 基本工资
        BigDecimal basieWage= Objects.isNull(wageOperate.getBasiWage())?BigDecimal.ZERO:wageOperate.getBasiWage();
        //岗位津贴
        BigDecimal postAllowance= Objects.isNull(wageOperate.getPostAllowance())?BigDecimal.ZERO:wageOperate.getPostAllowance();
        //忠诚奖励
        BigDecimal loyaltyReward= Objects.isNull(wageOperate.getLoyaltyReward())?BigDecimal.ZERO:wageOperate.getLoyaltyReward();
        //技能考核
        BigDecimal skillAssessment= Objects.isNull(wageOperate.getSkillAssessment())?BigDecimal.ZERO:wageOperate.getSkillAssessment();
        //管理考核
        BigDecimal managementAssessment= Objects.isNull(wageOperate.getManagementAssessment())?BigDecimal.ZERO:wageOperate.getManagementAssessment();
        //安全奖
        BigDecimal safetyAward= Objects.isNull(wageOperate.getSafetyAward())?BigDecimal.ZERO:wageOperate.getSafetyAward();
        //绩效
        BigDecimal performance= Objects.isNull(wageOperate.getPerformance())?BigDecimal.ZERO:wageOperate.getPerformance();
        //全勤
        BigDecimal fullAttendance= Objects.isNull(wageOperate.getFullAttendance())?BigDecimal.ZERO:wageOperate.getFullAttendance();


        //出勤工资
        BigDecimal attendancePay = (basieWage.add(postAllowance).add(loyaltyReward).add(skillAssessment).add(managementAssessment).add(safetyAward).add(performance)).add(fullAttendance)
                .divide(new BigDecimal("30.00"),8, RoundingMode.DOWN).multiply(new BigDecimal((wageOperate.getAttendanceDays()+".00"))).setScale(2, RoundingMode.DOWN);
        wageOperate.setAttendanceWage(attendancePay);
        //应发工资  出勤工资+专项补贴
        //专项补贴
        BigDecimal specialSubsidy= Objects.isNull(wageOperate.getSpecialSubsidy())?BigDecimal.ZERO:wageOperate.getSpecialSubsidy();
        BigDecimal payDue=attendancePay.add(specialSubsidy).setScale(2,RoundingMode.DOWN);
        wageOperate.setPayDue(payDue);
        //应扣工资 社保+个税+罚款+其他
        //社保
        BigDecimal socialSecurity= Objects.isNull(wageOperate.getSocialSecurity())?BigDecimal.ZERO:wageOperate.getSocialSecurity();
        //个税
        BigDecimal personalIncomeTax= Objects.isNull(wageOperate.getPersonalIncomeTax())?BigDecimal.ZERO:wageOperate.getPersonalIncomeTax();
        // 罚款
        BigDecimal fine= Objects.isNull(wageOperate.getFine())?BigDecimal.ZERO:wageOperate.getFine();
        // 其他
        BigDecimal other= Objects.isNull(wageOperate.getOther())?BigDecimal.ZERO:wageOperate.getOther();
        BigDecimal deductibleWages = socialSecurity.add(personalIncomeTax).add(fine).add(other).setScale(2, RoundingMode.DOWN);

        //实发工资 应发工资-应扣工资
        BigDecimal realWages= payDue.subtract(deductibleWages);
        wageOperate.setRealWages(realWages);

        return wageOperate;
    }

    /**
     * 保存工资历史记录
     * @param renewalSearchVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult saveWageOperateHistory(RenewalSearchVO renewalSearchVO) {
        //获取需要计算工资的数据
        List<WageOperate> wageOperateList=wageOperateMapper.selectWageOperateListBySearchVo(renewalSearchVO);
        //判断是否已保存过
        WageOperateHistory wageOperateHistory = new WageOperateHistory();
        wageOperateHistory.setCompany(renewalSearchVO.getCompany());
        wageOperateHistory.setAttendanceDate(renewalSearchVO.getRenewalDate());
        List<WageOperateHistory> wageOperateHistories = iWageOperateHistoryService.selectWageOperateHistoryList(wageOperateHistory);
        if (!CollectionUtils.isEmpty(wageOperateHistories)){
            //  根据公司，考勤月份删除旧数据
            iWageOperateHistoryService.deleteWageOperateHistoryByCompanyAndAttendanceDate(renewalSearchVO.getCompany(),renewalSearchVO.getRenewalDate());
        }
        List<WageOperateHistory> wageOperateHistoryList = new ArrayList<>();
        //    保存
        for (WageOperate wageOperate : wageOperateList) {
            WageOperateHistory entity = new WageOperateHistory();
            BeanUtils.copyProperties(wageOperate,entity,new String[]{"id"});
            entity.setAttendanceDate(renewalSearchVO.getRenewalDate());
            //未公开
            entity.setIsPublic("0");
            wageOperateHistoryList.add(entity);
        }
        iWageOperateHistoryService.insertBathWageOperateHistory(wageOperateHistoryList);

        return AjaxResult.success();
    }

    /**
     * 公开当月薪资
     * @param renewalSearchVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult publicToStaff(RenewalSearchVO renewalSearchVO) {
        return iWageOperateHistoryService.publicToStaff(renewalSearchVO);
    }

    /**
     * 导出
     * @param renewalSearchVO
     */
    @Override
    public void export(HttpServletResponse response,RenewalSearchVO renewalSearchVO) {
        ThreadLocal<String> name = new ThreadLocal<>();
        List<WageOperate> data = wageOperateMapper.selectWageOperateListBySearchVo(renewalSearchVO);
        name.set("complexExport");
        ExcelHelper.opsExport(PoiWorkbookType.XLSX)
                .opsSheet(data)
                .autoNum()
                .opsHeader()
                // 复杂表头模式, 支持三种合并方式, 1数字坐标 2字母坐标 3Excel坐标
                .complex()
                // excel坐标
                .text("猴卫士节能环保科技有限公司-"+ renewalSearchVO.getCompany()+"-工资发放表-"+ renewalSearchVO.getRenewalDate().substring(0,4) +"年（"+renewalSearchVO.getRenewalDate().substring(5,renewalSearchVO.getRenewalDate().length())+"）月", "A1:Z1")
                .text("工资说明：出勤工资=出勤工资=各项合计/30天（每月统一按平均30天）*出勤天数；应发工资=出勤工资+专项考核；实发工资=应发工资-应扣工资；单位：元。","A2:Z2")
                .text("款项将直接汇入工资卡中。如对以下金额或考勤有异议，请在收到本工资单48小时内向上级主管提出异议，否则即视为认可无异议。","A3:Z3")
                // 字母坐标
                .text("序号", "4,5,A,A")
                // 数字坐标
                .text("部门", "4,5,B,B")
                .text("岗位", "4,5,C,C")
                .text("姓名", "4,5,D,D")
                .text("入职日期", "4,5,E,E")
                .text("离职日期", "4,5,F,F")
                .text("员工类型", "4,5,G,G")
                .text("基本工资", "4,5,H,H")
                .text("岗位津贴", "4,5,I,I")
                .text("忠诚奖励", "4,5,J,J")
                .text("技能考核", "4,5,K,K")
                .text("管理考核", "4,5,L,L")
                .text("安全奖", "4,5,M,M")
                .text("绩效", "4,5,N,N")
                .text("全勤", "4,5,O,O")
                .text("出勤天数", "4,5,P,P")
                .text("出勤工资", "4,5,Q,Q")
                .text("专项补贴", "4,5,R,R")
                .text("应发工资", "4,5,S,S")
                .text("应扣工资", "4,4,T,W")
                .text("社保", "5,5,T,T")
                .text("个税", "5,5,U,U")
                .text("罚款", "5,5,V,V")
                .text("其他", "5,5,W,W")
                .text("实发工资", "4,5,X,X")
                .text("备注", "4,5,Y,Y")
                .text("内部备注", "4,5,Z,Z")
                .done()
                .opsColumn()
                .field("departmentName").done()
                .field("postName").done()
                .field("staffName").done()
                .field("entryDate").pattern("yyyy-MM-dd").done()
                .field("leaveDate").pattern("yyyy-MM-dd").done()
                .field("staffType").done()
                .field("basiWage").done()
                .field("postAllowance").done()
                .field("loyaltyReward").done()
                .field("skillAssessment").done()
                .field("managementAssessment").done()
                .field("safetyAward").done()
                .field("performance").done()
                .field("fullAttendance").done()
                .field("attendanceDays").done()
                .field("attendanceWage").done()
                .field("specialSubsidy").done()
                .field("payDue").done()
                .field("socialSecurity").done()
                .field("personalIncomeTax").done()
                .field("fine").done()
                .field("other").done()
                .field("realWages").done()
                .field("remark").done()
                .field("internalNotes").done()
                .done()
                .opsFooter()
                .text("合计:", "A1:D1")
                // 公式应用
                .text(String.format("=SUM(Q6:Q%s)", 6 + data.size()-1), "1,1,Q,Q")
                .text(String.format("=SUM(S6:S%s)", 6 + data.size()-1), "1,1,S,S")
                .text(String.format("=SUM(T6:T%s)", 6 + data.size()-1), "1,1,T,T")
                .text(String.format("=SUM(U6:U%s)", 6 + data.size()-1), "1,1,U,U")
                .text(String.format("=SUM(V6:V%s)", 6 + data.size()-1), "1,1,V,V")
                .text(String.format("=SUM(W6:W%s)", 6 + data.size()-1), "1,1,W,W")
                .text(String.format("=SUM(X6:X%s)", 6 + data.size()-1), "1,1,X,X")
                .done()
                .done()
                .export(response,"工资.xlsx");
    }

}
